<template>
  <div>
    <Row :gutter="20">
      <i-col span="8">
        <Card>
          <p slot="title">
            <Icon type="md-construct"/>
            {{ treeTitle || titlePlaceholder }}
          </p>
          <div slot="extra">
            <Poptip title="你确定要删除吗?" placement="bottom-end" :confirm="true" @on-ok="handlePopOk"
                    class="mr1" v-show="$refs.tree && $refs.tree.getSelectedNodes().length > 0">
              <Button size="small" type="error" icon="md-remove" @click="handleDelete"/>
            </Poptip>
            <Button size="small" type="primary" icon="md-add" @click="handleAdd"/>
          </div>
          <Tree
            ref="tree"
            :data="treeData"
            @on-select-change="handleSelect"/>
        </Card>
      </i-col>
      <i-col span="16">
        <Card>
          <p slot="title">
            <Icon type="ios-information-circle-outline"/>
            {{ formModel.name || titlePlaceholder }}
          </p>
          <Form ref="form" :model="formModel" :rules="validates" :label-width="80">
            <template v-for="item in fields">
              <FormItem :label="item.label" :key="item.prop" :prop="item.prop">
                <Input v-if="isInput(item.type)" :disabled="item.ro?true:formDisabled" :type="item.type"
                       :placeholder="item.placeholder" v-model="formModel[item.prop]"/>

                <i-switch v-else-if="item.type === 'switch'" :disabled="item.ro?true:formDisabled"
                          :true-value="1" :false-value="0" v-model="formModel[item.prop]"/>
              </FormItem>
            </template>
            <FormItem>
              <Button :disabled="formDisabled" type="primary" @click="handleSubmit" class="mr1">提交</Button>
              <Button :disabled="formDisabled" @click="handleCancel">取消</Button>
            </FormItem>
          </Form>
        </Card>
      </i-col>
    </Row>
  </div>
</template>
<script>
export default {
  name: 'TreeForm',
  data() {
    return {
      treeData: [],
      formModel: {},
      formDisabled: true,
      titlePlaceholder: 'Unknown'
    }
  },
  props: {
    value: {
      type: Array,
      default() {
        return []
      }
    },
    treeTitle: {
      type: String
    },
    fields: {
      type: Array,
      default() {
        return []
      }
    },
    validates: {
      type: Object,
      default() {
        return {}
      }
    }
  },
  watch: {
    value: function() {
      const process = (data) => {
        return data.map(({ children = [], ...data }) => {
          if (children.length > 0) {
            children = process(children)
          }
          return { title: data.name, data, children }
        })
      }
      this.treeData = process(this.value)
    }
  },
  computed: {
    treeMap: function() {
      let map = {}
      const toMap = (data) => {
        data.forEach(it => {
          map[it.data.id] = it
          it.children.length > 0 && toMap(it.children)
        })
      }
      toMap(this.treeData)

      return map
    },
    model: function() {
      let m = {}
      this.fields.forEach(it => {
        m[it.prop] = it.defaultValue
      })
      return m
    }
  },
  methods: {
    isInput(type) {
      return !type || ['text', 'password', 'textarea', 'url', 'email', 'date'].includes(type)
    },
    handleSelect([nodeData]) {
      // console.log('Selected nodeData: ', (nodeData))
      if (nodeData) {
        this.formModel = Object.assign({}, nodeData.data)
        this.formDisabled = false
      } else {
        this.handleCancel()
      }
    },
    handleAdd() {
      const [nodeData] = this.$refs.tree.getSelectedNodes()

      this.formModel = Object.assign({}, this.model)
      nodeData && ({ id: this.formModel.pid, name: this.formModel.parentName } = nodeData.data)
      this.formModel.level = this.generateLevel(nodeData)
      this.formDisabled = false
    },
    generateLevel(nodeData) {
      const isRoot = !nodeData

      const prefix = isRoot ? '' : this.treeMap[nodeData.data.id].data.level
      const ranges = (isRoot ? this.treeData : nodeData.children).map(it => it.data.level)
      if (ranges.length === 0) return `${prefix}000001`

      const max = ranges.reduce((a, b) => a > b ? a : b).substr(-6)
      let next = Number.parseInt(max) + 1
      const maximum = 999999
      if (next > maximum) {
        throw new Error('The maximum is 999999')
      }
      next = String(next)

      return `${prefix}${'0'.repeat(6 - next.length)}${next}`
    },
    handleNode(data) {
      let exist = this.treeMap[data.id]
      if (exist) {
        this.$set(exist, 'title', data.name)
        this.$set(exist, 'data', data)
      } else {
        (data.pid ? this.treeMap[data.pid].children : this.treeData).push({ title: data.name, data, children: [] })

        // manually click
        setTimeout(() => {
          const nodeData = this.treeMap[data.id]
          nodeData.data.pid && this.$set(this.treeMap[nodeData.data.pid], 'expand', true)
          this.$refs.tree.$emit('on-selected', nodeData.nodeKey)
        })
      }
    },
    handlePopOk() {
      const [nodeData] = this.$refs.tree.getSelectedNodes()

      if (nodeData.data.pid) {
        const ind = this.treeMap[nodeData.data.pid].children.findIndex(it => it.data.id === nodeData.data.id)
        this.treeMap[nodeData.data.pid].children.splice(ind, 1)
      } else {
        const ind = this.treeData.findIndex(it => it.data.id === nodeData.data.id)
        this.treeData.splice(ind, 1)
      }

      // process form
      this.handleCancel()
    },
    handleDelete() {
      this.$refs.tree.getSelectedNodes().length === 0 && event.stopPropagation()
    },
    handleSubmit() {
      this.$refs.form.validate(valid => {
        if (valid) {
          this.$emit('on-form-submit', this.formModel, (newData) => {
            this.handleNode(newData)
          })
        }
      })
    },
    handleCancel() {
      const [nodeData] = this.$refs.tree.getSelectedNodes()
      if (nodeData) {
        this.$refs.tree.$emit('on-selected', nodeData.nodeKey)
      } else {
        this.$refs.form.resetFields()
        this.formModel = Object.assign({}, this.model)
        this.formDisabled = true
      }
    }
  }
}
</script>

<style>
.mr1 { margin-right: 8px }
</style>
